package runtime;

public class ArrayType implements Type {
	public final Type baseType;
	public final int lowerBound;
	public final int upperBound;
	public final Type indexType;
	
	public ArrayType(Type baseType, int lowerBound, int upperBound, Type indexType) {
		this.baseType = baseType;
		this.lowerBound = lowerBound;
		this.upperBound = upperBound;
		this.indexType = indexType;
	}

	@Override
	public String platformName() {
		return basemostType() + "[" + elementCount() + "]";
	}
	
	private Type basemostType() {
		if (baseType instanceof ArrayType) {
			return ((ArrayType) (baseType)).basemostType();
		} else {
			return baseType;
		}
	}
	
	private int elementCount() {
		int myCount = upperBound - lowerBound + 1;
		if (baseType instanceof ArrayType) {
			return myCount * ((ArrayType) (baseType)).elementCount();
		} else {
			return myCount;
		}
	}
	
	private String uncast(int value, Type type) {
		if (type == BooleanType.instance) {
			return value == 0 ? "false" : "true";
		} else if (type == CharType.instance) {
			return "'" + (char) (value) + "'";
		} else {
			return String.valueOf(value);
		}
	}
	
	@Override
	public String toString() {
		return "array [" + uncast(lowerBound, indexType) + ".." 
	           + uncast(upperBound, indexType) + " of " + baseType;
	}
	
	@Override
	public boolean isNumber() {
		return false;
	}
	
	@Override
	public int numberLevel() {
		throw new UnsupportedOperationException();
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((baseType == null) ? 0 : baseType.hashCode());
		result = prime * result
				+ ((indexType == null) ? 0 : indexType.hashCode());
		result = prime * result + lowerBound;
		result = prime * result + upperBound;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		ArrayType other = (ArrayType) obj;
		if (baseType == null) {
			if (other.baseType != null) {
				return false;
			}
		} else if (!baseType.equals(other.baseType)) {
			return false;
		}
		if (indexType == null) {
			if (other.indexType != null) {
				return false;
			}
		} else if (!indexType.equals(other.indexType)) {
			return false;
		}
		if (lowerBound != other.lowerBound) {
			return false;
		}
		if (upperBound != other.upperBound) {
			return false;
		}
		return true;
	}
}
